/*
 * @Descripttion : Carousel 走马灯
 * @Versions     : 0.1
 * @Author       : foxui team
 * @Date         : 2022-05-29 21:38:33
 * @LastEditors  : QianFox Team
 * @LastEditTime : 2024-08-07 09:15:32
 */

import { _typeOf } from './common/utils';
import { _foxId } from './common/variables';
import { _isIntNum, _throttle } from './common/utils';
import { observer } from './common/observer';

let carouselSetInt = {};

function Carousel() {
    this.CONTAINER = '.foxui-carousel';
    this.VERTICAL = '.foxui-type-vertical';
    this.WRAPPER = '.foxui-carousel-wrapper';
    this.PAGINATION = '.foxui-carousel-pagination';
    this.SLIDE = '.foxui-carousel-slide';
    this.INDICATOR = '.foxui-pagination-indicator';
    this.PREV_BUTTON = '.foxui-carousel-button-prev';
    this.NEXT_BUTTON = '.foxui-carousel-button-next';
}

Carousel.prototype._init = function () {
    this._initCarousel();
    this._autoplay();
    this._bindClickIndicator();
    this._bindClickButton();
    this._bindResize();
    this._bindMouseEvent();
};

Carousel.prototype.init = function (el) {
    if (el && el !== '.foxui-carousel') {
        this._initCarousel(el);
        this._autoplay(el);
    }
};

/**
 * @description: 初始化 carousel
 * @return {*}
 * @Date: 2022-05-30 16:12:08
 */
Carousel.prototype._initCarousel = function (el) {
    let that = this,
        $container = el ? $(el) : $(this.CONTAINER);

    $container.each(function () {
        let $this = $(this),
            $pagination = $this.children(that.PAGINATION),
            $slide = $this.find(that.SLIDE),
            paginationId = $this.attr('data-pagination'),
            $diyPagination = $(`#${paginationId}`),
            isVertical = $this.is(that.VERTICAL),
            len = $slide.length,
            effect = $this.attr('data-effect'),
            foxId = _foxId();

        $this.attr('data-foxid', foxId);

        // 初始化 wrapper
        if (isVertical) {
            let height = parseInt($this.height());
            $slide.css('height', height + 'px');
            // 切换效果
            if (effect === 'fade') {
                $slide.each(function (index) {
                    $(this).css({
                        position: 'relative',
                        top: `-${height * index}px`,
                        opacity: index === 0 ? 1 : 0,
                        transition: 'opacity 500ms ease 0s, visibility 500ms ease 0s',
                    });
                });
            }
        } else {
            let width = parseInt($this.width());
            $slide.css('width', width + 'px');
            // 切换效果
            if (effect === 'fade') {
                $slide.each(function (index) {
                    $(this).css({
                        position: 'relative',
                        left: `-${width * index}px`,
                        opacity: index === 0 ? 1 : 0,
                        transition: 'opacity 500ms ease 0s, visibility 500ms ease 0s',
                    });
                });
            }
        }
        $slide.eq(0).addClass('is-active');

        // 初始化 pagination
        if ($diyPagination.length > 0) {
            $diyPagination.empty();
            for (let i = 0; i < len; i++) {
                $diyPagination.append(
                    `<div class="foxui-pagination-indicator${i === 0 ? ' is-active' : ''}"></div>`
                );
            }
        } else {
            $pagination.empty();
            for (let i = 0; i < len; i++) {
                $pagination.append(
                    `<div class="foxui-pagination-indicator${i === 0 ? ' is-active' : ''}"></div>`
                );
            }
        }

        // 绑定页面可见与不可见
        that._bindVisibilityChangeEvent($this);
    });
};

/**
 * @description: 绑定点击indicator切换
 * @return {*}
 * @Date: 2022-05-30 16:11:19
 */
Carousel.prototype._bindClickIndicator = function () {
    let that = this;
    $(document).on('click', this.INDICATOR, function () {
        let $this = $(this),
            $container = null,
            index = $this.index(),
            isVertical = null,
            isActive = $this.is('.is-active');

        let $pagination = $this.closest(that.PAGINATION),
            id = $pagination.attr('id');

        if (id) {
            $container = $(`${that.CONTAINER}[data-pagination=${id}]`);
        } else {
            $container = $this.closest(that.CONTAINER);
        }
        isVertical = $container.is(that.VERTICAL);

        let effect = $container.attr('data-effect');
        if (effect === 'fade') {
            that._fadeSwitch({
                $container,
                $indicator: $this,
                isActive,
                index,
            });
        } else {
            that._slideSwitch({
                $container,
                $indicator: $this,
                isActive,
                isVertical,
                index,
            });
        }
    });
};

/**
 * @description: 绑定点击 next prev 按钮切换
 * @return {*}
 * @Date: 2022-05-31 11:16:07
 */
Carousel.prototype._bindClickButton = function () {
    let that = this;
    const throttleSlideSwitch = _throttle(this._slideSwitch, 500).bind(this);

    $(document).on('click', this.PREV_BUTTON, function () {
        let $this = $(this),
            $container = null,
            $indicator = null,
            isVertical = $this.is(that.VERTICAL),
            effect = null,
            isActive = false,
            index = null,
            btnId = $this.attr('id');

        if (btnId) {
            $container = $(`${that.CONTAINER}[data-prev=${btnId}]`);
        } else {
            $container = $this.closest(that.CONTAINER);
        }
        isVertical = $container.is(that.VERTICAL);
        effect = $container.attr('data-effect');

        // index
        let $slideList = $container.find(that.SLIDE),
            i = $slideList.filter('.is-active').index(),
            len = $slideList.length;
        if (i > 0) {
            index = i - 1;
        } else {
            index = len - 1;
        }

        // $indicator
        let paginationId = $container.attr('data-pagination');
        if (paginationId) {
            $indicator = $(`#${paginationId}`).find(that.INDICATOR).eq(index);
        } else {
            $indicator = $container.find(that.INDICATOR).eq(index);
        }

        if (effect === 'fade') {
            that._fadeSwitch({
                $container,
                $indicator,
                isActive,
                index,
            });
        } else {
            throttleSlideSwitch({
                $container,
                $indicator,
                isActive,
                isVertical,
                index,
            });
        }
    });

    $(document).on('click', this.NEXT_BUTTON, function () {
        let $this = $(this),
            $container = null,
            $indicator = null,
            isVertical = $this.is(that.VERTICAL),
            effect = null,
            isActive = false,
            index = null,
            btnId = $this.attr('id');

        if (btnId) {
            $container = $(`${that.CONTAINER}[data-next=${btnId}]`);
        } else {
            $container = $this.closest(that.CONTAINER);
        }
        isVertical = $container.is(that.VERTICAL);
        effect = $container.attr('data-effect');

        // index
        let $slideList = $container.find(that.SLIDE),
            i = $slideList.filter('.is-active').index(),
            len = $slideList.length;
        if (i < len - 1) {
            index = i + 1;
        } else {
            index = 0;
        }

        // $indicator
        let paginationId = $container.attr('data-pagination');
        if (paginationId) {
            $indicator = $(`#${paginationId}`).find(that.INDICATOR).eq(index);
        } else {
            $indicator = $container.find(that.INDICATOR).eq(index);
        }

        if (effect === 'fade') {
            that._fadeSwitch({
                $container,
                $indicator,
                isActive,
                index,
            });
        } else {
            throttleSlideSwitch({
                $container,
                $indicator,
                isActive,
                isVertical,
                index,
            });
        }
    });
};

/**
 * @description: 绑定鼠标移入、移出
 * @return {*}
 * @Date: 2022-07-31 22:36:28
 */
Carousel.prototype._bindMouseEvent = function () {
    let that = this;

    $(document).on('mouseover', this.CONTAINER, function () {
        that._stop($(this));
    });

    $(document).on('mouseout', this.CONTAINER, function () {
        that._start($(this));
    });
};

/**
 * @description: 绑定页面可见与不可见时
 * @param {*} el
 * @return {*}
 * @Date: 2023-12-12 11:08:56
 */
Carousel.prototype._bindVisibilityChangeEvent = function ($el) {
    let that = this;

    document.addEventListener('visibilitychange', function () {
        if (document.hidden) {
            // 页面不可见
            that._stop($el);
        } else {
            // 页面可见
            that._start($el);
        }
    });
};

/**
 * @description: 停止切换
 * @param {*} el
 * @return {*}
 * @Date: 2023-12-12 11:12:55
 */
Carousel.prototype._stop = function ($el) {
    let foxId = $el.attr('data-foxid'),
        keyList = Object.keys(carouselSetInt),
        index = keyList.indexOf(foxId);

    if (index !== -1) {
        clearInterval(carouselSetInt[foxId]);
        carouselSetInt[foxId] = null;
    }
};

/**
 * @description: 开始切换
 * @return {*}
 * @Date: 2023-12-12 11:13:11
 */
Carousel.prototype._start = function ($el) {
    let foxId = $el.attr('data-foxid'),
        keyList = Object.keys(carouselSetInt),
        index = keyList.indexOf(foxId);

    if (index !== -1) {
        this._autoplay($(`${this.CONTAINER}[data-foxid=${foxId}]`));
    }
};

/**
 * @description: 自动切换
 * @param {*} el
 * @return {*}
 * @Date: 2022-05-30 16:11:30
 */
Carousel.prototype._autoplay = function (el) {
    let that = this,
        $container = el ? $(el) : $(this.CONTAINER);

    $container.each(function () {
        let $this = $(this),
            autoplay = $this.attr('data-autoplay'),
            foxId = $this.attr('data-foxid');

        if (_isIntNum(autoplay)) {
            let timer = setInterval(
                () => {
                    let $container = $this,
                        $indicator = null,
                        isActive = false,
                        isVertical = $this.is(that.VERTICAL),
                        index = null;

                    let $paginationId = $this.attr('data-pagination'),
                        $pagination = $paginationId
                            ? $(`#${$paginationId}`)
                            : $this.children(that.PAGINATION),
                        $indicatorList = $pagination.find(that.INDICATOR),
                        $slideList = $this.find(that.SLIDE),
                        len = $slideList.length,
                        i = $slideList.filter('.is-active').index();

                    if (i < len - 1) {
                        index = i + 1;
                    } else {
                        index = 0;
                    }

                    $indicator = $indicatorList.eq(index);
                    // 调用切换
                    let effect = $container.attr('data-effect');
                    if (effect === 'fade') {
                        that._fadeSwitch({
                            $container,
                            $indicator,
                            isActive,
                            index,
                        });
                    } else {
                        that._slideSwitch({
                            $container,
                            $indicator,
                            isActive,
                            isVertical,
                            index,
                        });
                    }
                },
                parseInt(autoplay) > 800 ? parseInt(autoplay) : 3000
            );
            carouselSetInt[foxId] = timer;
        }
    });
};

/**
 * @description: 滑动切换面板
 * @param {*} container
 * @param {*} indicator
 * @param {*} isActive
 * @param {*} isVertical
 * @param {*} index
 * @return {*}
 * @Date: 2022-05-30 15:26:13
 */
Carousel.prototype._slideSwitch = function ({
    $container,
    $indicator,
    isActive,
    isVertical,
    index,
}) {
    let $wrapper = $container.children(this.WRAPPER),
        $slide = $wrapper.find(this.SLIDE).eq(index);

    if (!isActive) {
        // 面板切换
        if (isVertical) {
            let height = parseInt($container.height());
            $wrapper
                .animate({ 'transition-duration': '500ms' }, 0, function () {
                    $(this).css({ transform: `translate3d(0px, -${height * index}px, 0px)` });
                })
                .animate({ 'transition-duration': '0ms' }, 500);
        } else {
            let width = parseInt($container.width());
            $wrapper
                .animate({ 'transition-duration': '500ms' }, 0, function () {
                    $(this).css({ transform: `translate3d(-${width * index}px, 0px, 0px)` });
                })
                .animate({ 'transition-duration': '0ms' }, 500);
        }

        // indicator 切换
        $indicator.siblings(this.INDICATOR).filter('.is-active').removeClass('is-active');
        $indicator.addClass('is-active');
        $slide.siblings(this.SLIDE).filter('.is-active').removeClass('is-active');
        $slide.addClass('is-active');

        // 发布消息
        let subid = $container.attr('data-subid');
        if (subid) observer._publiced('custom', index, subid, 'change');
    }
};

/**
 * @description: 渐隐切换
 * @param {*} container
 * @param {*} indicator
 * @param {*} isActive
 * @param {*} index
 * @return {*}
 * @Date: 2022-05-31 09:27:42
 */
Carousel.prototype._fadeSwitch = function ({ $container, $indicator, isActive, index }) {
    if (!isActive) {
        let $slideList = $container.find(this.SLIDE),
            $slide = $slideList.eq(index);

        $slide.siblings(this.SLIDE).filter('.is-active').removeClass('is-active').css('opacity', 0);
        $slide.addClass('is-active').css('opacity', 1);
        // indicator 切换
        $indicator.siblings(this.INDICATOR).filter('.is-active').removeClass('is-active');
        $indicator.addClass('is-active');

        // 发布消息
        let subid = $container.attr('data-subid');
        if (subid) observer._publiced('custom', index, subid, 'change');
    }
};

/**
 * @description: 绑定当窗口大小改变时，重置 slid 尺寸及修正 wrapper 的位移
 * @return {*}
 * @Date: 2022-05-30 22:49:15
 */
Carousel.prototype._bindResize = function () {
    let that = this;
    $(window).on('resize', function () {
        $(that.CONTAINER).each(function () {
            let $this = $(this),
                $wrapper = $this.children(that.WRAPPER),
                $slide = $this.find(that.SLIDE),
                isVertical = $this.is(that.VERTICAL),
                index = $slide.filter('.is-active').index(),
                effect = $this.attr('data-effect');

            if (isVertical) {
                let height = parseInt($this.height());
                $slide.css('height', height + 'px');

                if (effect === 'fade') {
                    $slide.each(function (index) {
                        $(this).css({ top: `-${height * index}px` });
                    });
                } else {
                    $wrapper.css({ transform: `translate3d(0px, -${height * index}px, 0px)` });
                }
            } else {
                let width = parseInt($this.width());
                $slide.css('width', width + 'px');

                if (effect === 'fade') {
                    $slide.each(function (index) {
                        $(this).css({ left: `-${width * index}px` });
                    });
                } else {
                    $wrapper.css({ transform: `translate3d(-${width * index}px, 0px, 0px)` });
                }
            }
        });
    });
};

/**
 * @description: 添加订阅者 --> 改变时
 * @param {*} subid
 * @param {*} callback
 * @return {*}
 * @Date: 2022-05-31 19:35:08
 */
Carousel.prototype.onChange = function (subid, callback) {
    if (subid && _typeOf(callback) === 'function') {
        observer._subscribe('custom', callback, subid, 'change');
    }
};

/**
 * @description: 取消订阅 --> 改变时
 * @param {*} subid
 * @return {*}
 * @Date: 2022-05-31 19:35:11
 */
Carousel.prototype.unChange = function (subid) {
    if (subid) observer._unsubscribe('custom', null, subid, 'change');
};

let carousel = new Carousel();

export { carousel };
